#ifndef __CAPIOBJ_H
#define __CAPIOBJ_H

#include <memory>
#include <string>
#include <vector>
#include <map>
#include <fstream>
#include <boost/shared_ptr.hpp>
#include "wavdevice.h"
#include "capi20.h"

//==========================================================================>>
// Helper log output function
//==========================================================================>>

void logout( std::ostream *pos, const char *szFormat, ... );

//==========================================================================>>
// CAPI controller profile (decoded)
//==========================================================================>>

struct CAPIControllerProfile
{
    word wNumberOfBChannels;

    bool bSupportInternalController;
    bool bSupportExternalEquipment;
    bool bSupportHandset;
    bool bSupportDTMF;
    bool bSupportSupplementaryServices;
    bool bSupportChannelAllocation;
    bool bSupportParameterBChannelOperation;
    bool bSupportLineInterconnect;

    bool bB1Support64;
    bool bB1Support64BitTransparent;
    bool bB1SupportV110Asynch;
    bool bB1SupportV110Synch;
    bool bB1SupportT30;
    bool bB1Support64Inverted;
    bool bB1Support56BitTransparent;
    bool bB1SupportModemNegot;
    bool bB1SupportModemAsynch;
    bool bB1SupportModemSynch;

    bool bB2SupportX75;
    bool bB2SupportTransparent;
    bool bB2SupportSDLC;
    bool bB2SupportQ921;
    bool bB2SupportT30;
    bool bB2SupportPPP;
    bool bB2SupportTransparentIgnoreB1;
    bool bB2SupportModemErrCorrCompr;
    bool bB2SupportX75V42bis;
    bool bB2SupportV120Asynch;
    bool bB2SupportV120AsynchV42bis;
    bool bB2SupportV120BitTransparent;
    bool bB2SupportQ921SAPISelection;

    bool bB3SupportTransparent;
    bool bB3SupportT90;
    bool bB3SupportISO8208;
    bool bB3SupportX25;
    bool bB3SupportT30;
    bool bB3SupportT30Ext;
    bool bB3SupportModem;

    byte memCAPIInternalInformation[24];
    byte memManufacturerSpecificInformation[20];
};

class CAPI;

//==========================================================================>>
// CAPI connection
//==========================================================================>>

class Connection
{
    friend CAPI;

private:
    Connection( Connection& );
    const Connection& operator=( Connection& );

public:
    Connection( const CAPI& capi, word plci ) : m_capi(capi), m_plci(plci), m_ncci(0), pWavDevice(0), posRecord(0), nBChannel(-1), m_state(stActive) {}
    ~Connection() { DeactivateRecording(); DeactivateWavOutput(); }
    void SetNCCI( word ncci ) { if ( ncci == 0 ) { DeactivateWavOutput(); DeactivateRecording(); } m_ncci = ncci; }
    void DataB3Ind( const _API_MSG& msg );

    void Alert( byte *byteChannelIdentification = 0 /* ETS 300 102-1, sect. 4.5.13 */ );
    void ConnectB3();
    dword SendData( word hData, byte *memData, dword nDataLen );
    void AdvancedSendData( byte *memData, dword nDataLen );
    void ResetB3();
    void DisconnectB3();
    void Info( char *szCalledPartyNumber, byte *memAdditionalInfo );
    void Disconnect();
    void HoldRetrieve( bool bHold );
    void ECT_ViaCAPI( word plci );
    void ECT_Manually();
    void CD_ViaCAPI( char *szToNumber );
    void SS_Manually( byte byteOperation );
    void Facility_Manually( byte *aIE, long dwSize );
    void EctLinkIdRequest_Manually();
    void StartStopDTMFListen( bool bStart );
    void SendDTMF( char *szDTMF );
    void SuspendResume( char *szCallIdentity, bool bSuspend );
    void SelectBProtocol( byte *memBProtocol );

    void LiConnectSimple( word plci );
    void LiDisconnectSimple( word plci );

    void ConnectResp( word wXRef, byte *byteChannelIdentification = 0 /* ETS 300 102-1, sect. 4.5.13 */ );

    void ActivateWavOutput() { DeactivateWavOutput(); pWavDevice = new WaveOutPcm16MonoDevice( 4096 ); }
    void DeactivateWavOutput() { delete pWavDevice; pWavDevice = 0; }

    void ActivateRecording( const char *szFilename ) { DeactivateRecording(); posRecord = new std::ofstream( szFilename, std::ios::out | std::ios::binary ); }
    void DeactivateRecording() { if ( posRecord ) posRecord->close(); delete posRecord; posRecord = 0; }

    word GetBChannel() { return nBChannel; }
    word GetPLCI() { return m_plci; }

    enum State { stActive, stHeld };

    State GetState() { return m_state; }

private:
    const CAPI& m_capi;
    word m_plci;
    word m_ncci;
    WaveOutPcm16MonoDevice *pWavDevice;
    std::ofstream *posRecord;
    word nBChannel;
    State m_state;
    byte bufRecording[2048];

    void ConnectActiveResp( word wXRef );
    void ConnectB3Resp( word wXRef );
    void ConnectB3ActiveResp( word wXRef );
    void ConnectB3T90ActiveResp( word wXRef );
    void DataB3Resp( word wXRef, word wDataHandle );
    void ResetB3Resp( word wXRef );
    void DisconnectB3Resp( word wXRef );
    void DisconnectResp( word wXRef );
    void InfoResp( word wXRef );
    void FacilityResp( word wXRef, word wFacilitySelector, word wFacility );
};

//==========================================================================>>
// CAPI base
//==========================================================================>>

class CAPI
{
    friend Connection;

public:
    struct XCAPIError {
        dword errcode;
        char msg[256];
        XCAPIError( char *functionName, dword _errcode ) : errcode(_errcode) {
            if ( strlen(functionName) > 100 )
                strcpy( functionName+97, "..." );
            _snprintf( msg, sizeof msg, "Function %s failed: error code = 0x%08x", errcode );
            msg[sizeof msg-1] = '\0';
        }
    };

    CAPI( unsigned long dwController, unsigned short wInfoMask, const char *szOwnNumber, bool bAutoAnswer, bool bAutoConnectResponse, unsigned long maxbDataLen, std::ostream *plogstream, void (*cb)(void *, const _API_MSG& msg ), void *cbparam );
    ~CAPI();

    word GetNumberOfControllers();
    void Initialize( dword maxbDataLen, bool bForceReset );
    bool isInitialized() { return bInitialized; }

    void Listen( word wInfoMask, dword dwCIPMask );
    void GetSupportedServices();
    void Connect( word wCIPValue, const char *szNumber, byte cTypeAndPlan, char *szCallingNumber, byte *byteChannelIdentification = 0 /* ETS 300 102-1, sect. 4.5.13 */ );  // Simplified version of the function
    void ConnectLeased( int nBchannel );
    void Connect( word wCIPValue,                                         // Full version of the function
                  char *szCalledPartyNumber, char *szCallingPartyNumber,
                  char *szCalledPartySubaddress, char *szCallingPartySubaddress,
                  byte *byteChannelIdetntification = 0 );
    void CF_Manually( byte type, char *szMSN, char *szForwardedToNumber );
    void CFInterrogate_Manually( byte type, char *szMSN );

    void LIGetSupportedServices( word plci = 0 );

    void ConnectDChannel( word nBProtocolIndex );  // To get direct access to Layer 2 of the D channel

    typedef std::map<word,boost::shared_ptr<Connection> > ConnectionMap;
    ConnectionMap conns;     // List of physical connections

    bool IsValidPLCI( word plci ) { return conns.find( plci ) != conns.end(); }

    static byte *string2struct( byte *p, char *szString );

    char szManufacturer[64];
    char szSerialNumber[8];
    dword dwCAPIMajor;                     // Version Info
    dword dwCAPIMinor;                     // Version Info
    dword dwManufacturerMajor;             // Version Info
    dword dwManufacturerMinor;             // Version Info
    word wNumberOfControllers;
    dword wApplId;
    std::vector<CAPIControllerProfile> ControllerProfiles;
    word wLastConnectionIndXRef;
    dword maxBDataLen;

private:
    dword dwController;
    word wInfoMask;
    std::string sOwnNumber;
    bool bAutoAnswer;
    bool bAutoConnectResponse;
    void (*cb)( void *, const _API_MSG& );
    void *cbparam;

    bool bInitialized;

    _API_MSG m_msg;  // The connection currently being initiated (currently one at a time)

    HANDLE hTerminateEvent;
    HANDLE hIncomingMessageHandlingThread;
    HANDLE hSendingMutex;      // Used by SubmitCAPIMessage to order the calls from different threads
    HANDLE hDataConfEvent;     // Signaled when DATA_B3_CONF received

    dword SubmitCAPIMessage( _API_MSG& msg ) const;
    static DWORD CALLBACK IncomingMessageHandlingThread( void *_ );
    void HandleIncomingMessage( const _API_MSG& msg );

    std::ostream *plogstream;
    const byte *decodeNumber( const byte *p, int bCallingNumber ) const;
    const byte *dumpParameter( const byte *p ) const;

    void SpitToLog( const _API_MSG& msg ) const;
};

extern const unsigned char memBProtocol64[];
extern const unsigned char memBProtocolFaxSend[];
extern const unsigned char memBProtocolFaxRcv[];

extern const unsigned char memBProtocolRaw[][16];

#endif // __CAPIOBJ_H
